package com.parse.dxf.utils;

import com.parse.dxf.entity.PolylineInfo;
import com.parse.dxf.entity.RoomInfo;
import com.parse.dxf.entity.VertexInfo;
import org.kabeja.batik.tools.SAXPDFSerializer;
import org.kabeja.batik.tools.SAXPNGSerializer;
import org.kabeja.dxf.*;
import org.kabeja.dxf.helpers.Point;
import org.kabeja.parser.DXFParser;
import org.kabeja.parser.ParserBuilder;
import org.kabeja.svg.SVGContext;
import org.kabeja.svg.SVGGenerator;
import org.kabeja.xml.SAXGenerator;
import org.kabeja.xml.SAXPrettyOutputter;
import org.kabeja.xml.SAXSerializer;
import org.kabeja.xslt.SAXXMLSerializer;
import org.xml.sax.SAXException;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.HashMap;

public class DxfParseUtils {

    public static DXFDocument parseDxf(String filePath) {
        DXFDocument doc = null;
        try {
            InputStream inputStream = new FileInputStream(filePath);
            return parseDxf(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return doc;
    }


    public static DXFDocument parseDxf(InputStream inputStream) {
        DXFDocument doc = null;
        try {
            DXFParser parser = (DXFParser) ParserBuilder.createDefaultParser();
            parser.parse(inputStream, "UTF-8");
            doc = parser.getDocument();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //遍历图层
//        Iterator iter= doc.getDXFLayerIterator();
//
//        while (iter.hasNext()){
//            DXFLayer dxfLayer = (DXFLayer)iter.next();
//            String layerName=dxfLayer.getName();
//            readEntity(dxfLayer);
//        }

        return doc;
    }


    public static void readEntity(DXFLayer layer) {
        Iterator iter = layer.getDXFEntityTypeIterator();
        while (iter.hasNext()) {
            String s = (String) iter.next();
            getEntity(layer, s);
        }
    }

    //读取block
//    public static void readBlock(DXFDocument doc){
//        Iterator iter= doc.getDXFBlockIterator();
//        while (iter.hasNext()){
//            DXFBlock s=(DXFBlock) iter.next();
//        }
//    }

    //读取图层下的实体
    public static void getEntity(DXFLayer layer, String type) {
        List<DXFEntity> entities = layer.getDXFEntities(type);
        for (int i = 0; i < entities.size(); i++) {
            DXFEntity entity = entities.get(i);
            System.out.println(entity.getColor());
            String color = DXFColor.getRGBString(entity.getColor());
            System.out.println(color);
        }
    }


    //解析dxf文件中的房间信息
    public static List<RoomInfo> parseDxfRooms(DXFDocument doc, int dxf_id) {
        List<RoomInfo> roomInfos = new ArrayList<>();
        //遍历图层
        Iterator<DXFLayer> iter = doc.getDXFLayerIterator();
        while (iter.hasNext()) {
            DXFLayer dxfLayer = iter.next();
            String layerName = dxfLayer.getName();
            if (layerName.equals("RM") || layerName.equals("RM$")) {
                Iterator<String> eleiter = dxfLayer.getDXFEntityTypeIterator();
                while (eleiter.hasNext()) {
                    String type = eleiter.next();
                    if (type.equals("LWPOLYLINE") || type.equals("POLYLINE")) {
                        List<DXFEntity> entities = dxfLayer.getDXFEntities(type);
                        for (int i = 0; i < entities.size(); i++) {
                            DXFEntity entity = entities.get(i);
                            RoomInfo roomInfo = parseDxfRoom(entity);
                            if (roomInfo != null) {
                                roomInfo.setDxf_id(dxf_id);
                                roomInfos.add(roomInfo);
                            }
                        }
                    }
                }
            }
        }

        Iterator<DXFLayer> iter2 = doc.getDXFLayerIterator();
        while (iter2.hasNext()) {
            DXFLayer dxfLayer = iter2.next();
            String layerName = dxfLayer.getName();
            if (layerName.equals("RM-ID") || layerName.equals("RM$TXT")) {
                Iterator<String> eleiter = dxfLayer.getDXFEntityTypeIterator();
                while (eleiter.hasNext()) {
                    String type = eleiter.next();
                    if (type.equals("TEXT") || type.equals("MTEXT")) {
                        List<DXFText> entities = dxfLayer.getDXFEntities(type);
                        for (int i = 0; i < entities.size(); i++) {
                            DXFText text = entities.get(i);
                            Point p= text.getInsertPoint();

                            for (int j = 0; j < roomInfos.size(); j++) {
                                RoomInfo room=  roomInfos.get(j);
                                if(layerName.equals("RM-ID"))
                                {
                                    if(room.getRm_id()!=null) continue;
                                    boolean inside = MathUtils.isPointInPolygon(p.getX(),p.getY(), room.getPts());
                                    if (inside) {
                                        double area=MathUtils.calculatePolygonArea(room.getPts());
                                        room.setRm_id(text.getText());
                                        room.setName(text.getText());
                                        room.setArea((float) area);
                                    }
                                }
                                else
                                {
                                    if (room.getRm_id().isEmpty()) {
                                        boolean inside = MathUtils.isPointInPolygon(p.getX(),p.getY(), room.getPts());
                                        if (inside) {
                                            double area=MathUtils.calculatePolygonArea(room.getPts());
                                            room.setName(text.getText());
                                            room.setArea((float) area);
                                        }
                                    }

                                }
                            }
                        }
                    }
                }
            }
        }
        return roomInfos;
    }

    private static RoomInfo parseDxfRoom(DXFEntity entity) {
        if (entity instanceof DXFPolyline) {
            DXFPolyline polyline = (DXFPolyline) entity;
            int count = polyline.getVertexCount();
            if (count < 3) {
                return null;
            }
            RoomInfo roomInfo = new RoomInfo();
            PolylineInfo polylineInfo = _parsePolyLine(polyline,roomInfo);
            roomInfo.setPolyline(polylineInfo);
            return roomInfo;
        }
        return null;
    }


    private static boolean _isMirror(DXFPolyline entity) {
        return entity.getExtrusion().getZ() < 0;
    }


    private static PolylineInfo _parsePolyLine(DXFPolyline polyline,RoomInfo roomInfo) {
        int verticesCount = polyline.getVertexCount();
        boolean isMirror = _isMirror(polyline);
        PolylineInfo polylineInfo = new PolylineInfo();
        List<VertexInfo> entityVertices = new ArrayList<>();
        polylineInfo.setPolyline(entityVertices);

        double arcTessellationAngle = 10.0/ 180.0 * Math.PI;
        int minArcTessellationSubdivisions = 8;

        for (int i = 0; i < verticesCount; i++) {
            DXFVertex vertex = polyline.getVertex(i);
            VertexInfo vertexInfo = new VertexInfo();
            if (isMirror) {
                vertexInfo.setX(-(float) vertex.getX());
            } else {
                vertexInfo.setX((float) vertex.getX());
            }
            vertexInfo.setY((float) vertex.getY());
            vertexInfo.setZ((float) vertex.getZ());
            vertexInfo.setBulge((float) vertex.getBulge());
            polylineInfo.getPolyline().add(vertexInfo);
        }

        int startIdx = 0;
        List<VertexInfo> curVertices = null;

        roomInfo.setPts(new ArrayList<>());
        for (int vIdx = 1; vIdx <= verticesCount; vIdx++) {
            VertexInfo prevVtx = entityVertices.get(vIdx - 1);
            VertexInfo vtx;
            if (vIdx == verticesCount) {
                if (!((polyline.getFlags() & 1) == 1)) {
                    startIdx = _commitSegment(vIdx - 1, startIdx, verticesCount, entityVertices, curVertices, polylineInfo,  roomInfo);
                    break;
                }
                vtx = entityVertices.get(0);
            } else {
                vtx = entityVertices.get(vIdx);
            }

            if (prevVtx.getBulge() != 0) {
                if (curVertices == null || curVertices.isEmpty()) {
                    curVertices = new ArrayList<>(entityVertices.subList(startIdx, vIdx));
                }
                _generateBulgeVertices(curVertices, prevVtx, vtx, prevVtx.getBulge(), arcTessellationAngle, minArcTessellationSubdivisions);
            } else if (curVertices != null) {
                curVertices.add(vtx);
            }

            if (vIdx == verticesCount) {
                startIdx = _commitSegment(vIdx, startIdx, verticesCount, entityVertices, curVertices, polylineInfo, roomInfo);
                break;
            }
            //startIdx=  _commitSegment(vIdx,startIdx,verticesCount,entityVertices,curVertices,polylineInfo,polyline2Dpts);
        }
        return polylineInfo;
    }

    private static int _commitSegment(int endIdx, int startIdx, int verticesCount, List<VertexInfo> entityVertices, List<VertexInfo> curVertices, PolylineInfo polylineInfo, RoomInfo roomInfo) {
        if (endIdx == startIdx) {
            return startIdx;
        }
        boolean isClosed = false;
        roomInfo.getPts().clear();
        if (endIdx == verticesCount && startIdx == 0) {
            isClosed = true;
            if (curVertices == null||curVertices.isEmpty()) {
                Iterator<VertexInfo> eleiter = entityVertices.iterator();
                while (eleiter.hasNext()) {
                    VertexInfo p = eleiter.next();
                    roomInfo.getPts().add(p);
                }
            }
        } else if (endIdx == verticesCount - 1 && startIdx == 0) {
            if (curVertices == null||curVertices.isEmpty()) {
                Iterator<VertexInfo> eleiter = entityVertices.iterator();
                while (eleiter.hasNext()) {
                    VertexInfo p = eleiter.next();
                    roomInfo.getPts().add(p);
                }
            }
        } else if (endIdx == verticesCount) {
            if (curVertices == null||curVertices.isEmpty()) {

                Iterator<VertexInfo> eleiter = entityVertices.subList(startIdx, endIdx).iterator();
                while (eleiter.hasNext()) {
                    VertexInfo p = eleiter.next();
                    roomInfo.getPts().add(p);
                }
            }
        } else {
            if (curVertices == null||curVertices.isEmpty()) {
                Iterator<VertexInfo> eleiter = entityVertices.subList(startIdx, endIdx + 1).iterator();
                while (eleiter.hasNext()) {
                    VertexInfo p = eleiter.next();
                    roomInfo.getPts().add(p);
                }
            }
        }

        //refPolyline2Dpts=new ArrayList<> (vertices);

        polylineInfo.setColosed(isClosed);
        if (curVertices != null) {
            curVertices.clear();
            curVertices = null;
        }
        startIdx = endIdx;
        return startIdx;
    }


    private static void _generateBulgeVertices(List<VertexInfo> vertices, VertexInfo startVtx, VertexInfo endVtx, float bulge, double arcTessellationAngle, int minArcTessellationSubdivisions) {
        double a = 4 * Math.atan(bulge);
        double aAbs = Math.abs(a);
        if (aAbs < arcTessellationAngle) {
            vertices.add(endVtx);
            ;
            return;
        }
        double ha = a / 2;
        double sha = Math.sin(ha);
        double cha = Math.cos(ha);
        double d_x = endVtx.getX() - startVtx.getX();
        double d_y = endVtx.getY() - startVtx.getY();
        double dSq = d_x * d_x + d_y * d_y;
        if (dSq < Float.MIN_VALUE * 2) {
            return;
        }
        double sqrt_dSq = Math.sqrt(dSq);
        double R = sqrt_dSq / 2 / sha;
        d_x /= sqrt_dSq;
        d_y /= sqrt_dSq;
        double centerX = (d_x * sha - d_y * cha) * R + startVtx.getX();
        double centerY = (d_x * cha + d_y * sha) * R + startVtx.getY();

        double numSegments = Math.floor(aAbs / arcTessellationAngle);
        if (numSegments < minArcTessellationSubdivisions) {
            numSegments = minArcTessellationSubdivisions;
        }
        if (numSegments > 1) {
            double startAngle = Math.atan2(startVtx.getY() - centerY, startVtx.getX() - centerX);
            double step = a / numSegments;
            if (a < 0) {
                R = -R;
            }
            for (int i = 1; i < numSegments; i++) {
                double angle = startAngle + i * step;
                double x = centerX + R * Math.cos(angle);
                double y = centerY + R * Math.sin(angle);
                VertexInfo p = new VertexInfo();
                p.setX((float) x);
                p.setY((float) y);
                p.setZ(0);
                vertices.add(p);
            }
        }
        vertices.add(endVtx);
    }


    public  static <XMLGeneratorManager> void convertDXFToSVG(String dxfFilePath, String svgFilePath) throws FileNotFoundException, SAXException {
        try {
            DXFDocument dxfDocument = parseDxf(dxfFilePath);
            SAXGenerator generator = new SVGGenerator();
            // 输出SVG
            // SAXSerializer out = new SAXXMLSerializer();
            // 输出pdf
            SAXSerializer out = new SAXPDFSerializer();
            // 输出tiff
            // org.kabeja.xml.SAXSerialzer out =
            // org.kabeja.batik.tools.SAXTIFFSerializer();
            // 输出png
            // SAXSerializer out =new SAXPNGSerializer();
            // 输出jpg
            // org.kabeja.xml.SAXSerialzer out =
            // org.kabeja.batik.tools.SAXJEPGSerializer();
            OutputStream fileo = new FileOutputStream(svgFilePath);
            out.setOutput(fileo);
            generator.generate(dxfDocument, out, new HashMap());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
//        out.setOutput(fileo);
//        out.setOutput(fileo);
//        generator.generate(dxfDocument, out, new HashMap());
    }
}